#include "Camera.h"

// Initialize the instance to null
Camera* Camera::m_CamInstance = 0;

Camera::Camera(void)
{
}

Camera::~Camera(void)
{
}

Camera* Camera::GetInstance(void)
{
	// Create the instance if it doesn't exist
	if(!m_CamInstance)
		m_CamInstance = new Camera;

	// Return the singleton
	return m_CamInstance;
}

void Camera::DeleteInstance(void)
{
	if(m_pWorldSkybox)
	{
		///////////////////////////////////////////////////////////////////////////////////////////////
		// Delete the plane mesh for skybox
		if(m_pWorldSkybox->m_pMaterials != NULL)
		{
			delete m_pWorldSkybox->m_pMaterials;
		}

		if(m_pWorldSkybox->m_pMeshTextures)
		{
			for(DWORD j = 0; j < m_pWorldSkybox->m_dwNumMaterials; j++)
			{
				if(m_pWorldSkybox->m_pMeshTextures[j])
				{
					m_pWorldSkybox->m_pMeshTextures[j]->Release();
					m_pWorldSkybox->m_pMeshTextures[j] = NULL;
				}
			}

			delete[] m_pWorldSkybox->m_pMeshTextures;
		}

		if(m_pWorldSkybox->m_pMesh)
		{
			m_pWorldSkybox->m_pMesh->Release();
			m_pWorldSkybox->m_pMesh = NULL;
		}

		delete m_pWorldSkybox;
		m_pWorldSkybox = NULL;
	}
	///////////////////////////////////////////////////////////////////////////////////////////////

	// If instance exist, delete it!
	if(m_CamInstance)
	{
		delete m_CamInstance;
		m_CamInstance = NULL;
	}
}

void Camera::InitCamera(IDirect3DDevice9* m_pD3DDevice, int nHeight, int nWidth)
{
	// Initialize the D3D Device
	m_Device = m_pD3DDevice;

	// Screen size
	iScreenHeight = nHeight;
	iScreenWidth = nWidth;

	// Reset the matrices
	D3DXMatrixIdentity( &worldMat);
	D3DXMatrixIdentity( &scaleMat);
	D3DXMatrixIdentity( &rotXMat);
	D3DXMatrixIdentity( &rotYMat);
	D3DXMatrixIdentity( &transMat);

	// Set these 3 components to initialize your camera (required)
	m_eyePos = D3DXVECTOR3( 0.0f, 25.0f, 3.0f);
	m_lookAt = D3DXVECTOR3( 0.0f, 0.0f, 3.0f);
	m_upVec = D3DXVECTOR3( 0.0f, 0.0f, 100.0f);

	// Initialize the camera's view matrix
	D3DXMatrixLookAtLH(
		&m_viewMat,		// Returned camera view matrix
		&m_eyePos,		// Camera's eye position
		&m_lookAt,		// Which way camera is pointed
		&m_upVec);		// Which way is up

	// Initialize the projection matrix ("frustum")
	D3DXMatrixPerspectiveFovLH(
		&m_projMat,		// Returned projection matrix
		D3DXToRadian(65.0f),	// Field of view in radians
		(float)iScreenWidth / (float)iScreenHeight,	// Aspect ratio
		1.0f,		// Near plane
		300.0f);	// Far plane

	// Draw our camera outside the render routine
	m_Device->SetTransform( D3DTS_PROJECTION, &m_projMat);

	// Set up the view matrix
	m_Device->SetTransform( D3DTS_VIEW, &m_viewMat);

	// No skybox is created yet
	m_pWorldSkybox = NULL;

	// Get the instance of the ModelManager
	m_pModelManager = ModelManager::GetInstance();
}

void Camera::LoadSkybox(void)
{
	/////////////////////////////////////////////////////////////////////////////////////////////////////////
	// INITIALIZE THE SKYBOX
	// Start .x loading
	// Create the skybox structure
	m_pWorldSkybox = new s_Skybox();

	LPD3DXBUFFER p_D3DXMtrlBuffer;
	m_pWorldSkybox->m_dwNumMaterials = 0;

	// Load .x file
	D3DXLoadMeshFromX( "Models\\Skybox1.X", D3DXMESH_SYSTEMMEM,
		m_Device, NULL, &p_D3DXMtrlBuffer, NULL, &(m_pWorldSkybox->m_dwNumMaterials),
		&(m_pWorldSkybox->m_pMesh));

	D3DXMATERIAL* d3dxMaterials =
		(D3DXMATERIAL*)p_D3DXMtrlBuffer->GetBufferPointer();

	// Create an array based on the amount of materials we have
	m_pWorldSkybox->m_pMaterials = new D3DMATERIAL9[m_pWorldSkybox->m_dwNumMaterials];
	m_pWorldSkybox->m_pMeshTextures = new LPDIRECT3DTEXTURE9[m_pWorldSkybox->m_dwNumMaterials];

	// Load each material
	for( DWORD i = 0; i < m_pWorldSkybox->m_dwNumMaterials; i++)
	{
		m_pWorldSkybox->m_pMaterials[i] = d3dxMaterials[i].MatD3D;
		
		m_pWorldSkybox->m_pMaterials[i].Ambient = m_pWorldSkybox->m_pMaterials[i].Diffuse;

		m_pWorldSkybox->m_pMeshTextures[i] = NULL;
		if( d3dxMaterials[i].pTextureFilename)
			D3DXCreateTextureFromFile( m_Device,
				d3dxMaterials[i].pTextureFilename,
				&(m_pWorldSkybox->m_pMeshTextures[i]));
	}
}

void Camera::SwitchToBuildCam(void)
{
	// Get the current position of the player
	D3DXVECTOR3 &vecPlayerPos = m_pModelManager->GetPlayerPos();
	// Set the camera position and look at vector for building
	m_eyePos = D3DXVECTOR3(0.0f, 25.0f, vecPlayerPos.z);
	m_lookAt = D3DXVECTOR3(0.0f, 0.0f, vecPlayerPos.z);
	m_upVec = D3DXVECTOR3(0.0f, 0.0f, 100.0f);

	// Update the view matrix to the new positions
	D3DXMatrixLookAtLH(&m_viewMat,
		&m_eyePos,
		&m_lookAt,
		&m_upVec);

	// Set up the view matrix
	m_Device->SetTransform(D3DTS_VIEW, &m_viewMat);
}

void Camera::SwitchToFightCam(void)
{
	// Get the current position of the player
	D3DXVECTOR3 &vecPlayerPos = m_pModelManager->GetPlayerPos();
	// Set the camera position and look at vector for building
	m_eyePos = D3DXVECTOR3(0.0f, vecPlayerPos.y + 8.0f, vecPlayerPos.z);
	m_lookAt = D3DXVECTOR3(0.0f, 0.0f, vecPlayerPos.z);
	m_upVec = D3DXVECTOR3(0.0f, 0.0f, 100.0f);

	// Update the view matrix to the new positions
	D3DXMatrixLookAtLH(&m_viewMat,
		&m_eyePos,
		&m_lookAt,
		&m_upVec);

	// Set up the view matrix
	m_Device->SetTransform(D3DTS_VIEW, &m_viewMat);
}

void Camera::SwtichToBehindCam(void)
{
	// Get the current position of the player
	D3DXVECTOR3 &vecPlayerPos = m_pModelManager->GetPlayerPos();
	// Set the camera position and look at vectors for following the player
	m_eyePos = D3DXVECTOR3(vecPlayerPos.x, 3.0f, vecPlayerPos.z - 3.0f);
	m_lookAt = D3DXVECTOR3(vecPlayerPos.x, 0.0f, vecPlayerPos.z);
	m_upVec = D3DXVECTOR3(0.0f, 20.0f, 0.0f);

	// Update the view matrix to the new positions
	D3DXMatrixLookAtLH(&m_viewMat,
		&m_eyePos,
		&m_lookAt,
		&m_upVec);

	// Set up the view matrix
	m_Device->SetTransform(D3DTS_VIEW, &m_viewMat);
}

void Camera::UpdateCamera(bool bFightMode, bool bJumpComplete)
{
	// Get the current position of the player
	D3DXVECTOR3 &vecPlayerPos = m_pModelManager->GetPlayerPos();
	if(!bFightMode)
	{
		if(bJumpComplete)
		{
			// Set the camera to start following the player
			m_eyePos = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y + 3.0f, vecPlayerPos.z - 3.0f);
			m_lookAt = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y, vecPlayerPos.z);
		}
		else
		{
			// Keep the camera in the jump position
			m_eyePos = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y + 5.0f, vecPlayerPos.z - 0.5f);
			m_lookAt = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y, vecPlayerPos.z);
		}
	}
	else
	{
		// Set the camera to start following the player
		m_eyePos = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y + 8.0f, vecPlayerPos.z - 7.0f);
		m_lookAt = D3DXVECTOR3(vecPlayerPos.x, vecPlayerPos.y, vecPlayerPos.z);
	}

	// Update the view matrix to the new positions
	D3DXMatrixLookAtLH(&m_viewMat,
		&m_eyePos,
		&m_lookAt,
		&m_upVec);

	// Set up the view matrix
	m_Device->SetTransform(D3DTS_VIEW, &m_viewMat);
}

void Camera::ModifyCameraPos(float deltaTime, float fXChange, float fYChange, float fZChange)
{
	// Modify the position of the camera
	m_eyePos.x += fXChange * deltaTime;
	m_eyePos.y += fYChange * deltaTime;
	if(m_eyePos.y > 25.0f)
		m_eyePos.y = 25.0f;
	else if(m_eyePos.y < 0.0f)
		m_eyePos.y = 0.0f;
	m_eyePos.z += fZChange * deltaTime;
	// Modify the look at of the camera
	m_lookAt.x += fXChange * deltaTime;
	m_lookAt.z += fZChange * deltaTime;

	// Update the view matrix to the new positions
	D3DXMatrixLookAtLH(&m_viewMat,
		&m_eyePos,
		&m_lookAt,
		&m_upVec);

	// Set up the view matrix
	m_Device->SetTransform(D3DTS_VIEW, &m_viewMat);
}

void Camera::SetBoundaries(float fXNeg, float fXPos, float fZNeg, float fZPos)
{
	// Set the boundaries for the camera based on the level text file
	fXNegBound = fXNeg;
	fXBound = fXPos;
	fZNegBound = fZNeg;
	fZBound = fZPos;
}

D3DXVECTOR3* Camera::CreateRay(POINT pMousePoint, bool bFightMode)
{
	///////////////////////////////////////////////////////////////////////////////
	//Transform the screen space pick ray into 3D space
	D3DXVECTOR3 v;
	v.x =  ( ( ( 2.0f * pMousePoint.x ) / iScreenWidth  ) - 1 ) / m_projMat._11;
	v.y = -( ( ( 2.0f * pMousePoint.y ) / iScreenHeight ) - 1 ) / m_projMat._22;
	v.z =  1.0f;

	//apply the inverse of this matrix to create the ray
	D3DXMatrixInverse( &m, NULL, &m_viewMat );

	// Transform the screen space pick ray into 3D space
	vecRayDir.x  = v.x*m._11 + v.y*m._21 + v.z*m._31;
	vecRayDir.y  = v.x*m._12 + v.y*m._22 + v.z*m._32;
	vecRayDir.z  = v.x*m._13 + v.y*m._23 + v.z*m._33;
	vecRayOrigin.x = m._41;
	vecRayOrigin.y = m._42;
	vecRayOrigin.z = m._43;

	if(!bFightMode)
	{
		// Origin on y axis is 0 in lower level
		while(vecRayOrigin.y > 0)
		{
			vecRayOrigin.x += vecRayDir.x;
			vecRayOrigin.y += vecRayDir.y;
			vecRayOrigin.z += vecRayDir.z;
		}
	}
	else
	{
		// Origin on y axis is 10 in upper level
		while(vecRayOrigin.y > 10)
		{
			vecRayOrigin.x += vecRayDir.x;
			vecRayOrigin.y += vecRayDir.y;
			vecRayOrigin.z += vecRayDir.z;
		}
	}
	///////////////////////////////////////////////////////////////////////////////

	return &vecRayOrigin;
}

void Camera::RenderWorldPlane(void)
{
	///////////////////////////////////////////////////////////////////////////////
	// Render the world plane for skybox
	D3DXMatrixIdentity( &m_pWorldSkybox->scaleMat);
	D3DXMatrixIdentity( &m_pWorldSkybox->rotMat);
	D3DXMatrixIdentity( &m_pWorldSkybox->transMat);
	D3DXMatrixIdentity( &m_pWorldSkybox->worldMat);

	// Rotate, scale, and move the .x drawing
	D3DXMatrixScaling( &m_pWorldSkybox->scaleMat, 1.0f, 1.0f, 1.0f);
	D3DXMatrixRotationYawPitchRoll( &m_pWorldSkybox->rotMat, 0.0f, 0.0f, 0.0f);
	D3DXMatrixTranslation( &m_pWorldSkybox->transMat, 0.0f, -1.0f, 36.0f);

	// Scale & rotation * translation
	D3DXMatrixMultiply( &m_pWorldSkybox->scaleMat, &m_pWorldSkybox->rotMat, &m_pWorldSkybox->scaleMat);
	D3DXMatrixMultiply( &m_pWorldSkybox->worldMat, &m_pWorldSkybox->scaleMat, &m_pWorldSkybox->transMat);

	m_Device->SetTransform( D3DTS_WORLD, &m_pWorldSkybox->worldMat);

	// Start .x drawing
	for( DWORD i = 0; i < m_pWorldSkybox->m_dwNumMaterials; i++)
	{
		m_Device->SetMaterial( &(m_pWorldSkybox->m_pMaterials[i]));
		m_Device->SetTexture( 0, (m_pWorldSkybox->m_pMeshTextures[i]));

		m_pWorldSkybox->m_pMesh->DrawSubset(i);
	}
	
	m_Device->SetTexture(0, NULL);
}

void Camera::DestroySkybox(void)
{
	// Delete the mesh for skybox
	if(m_pWorldSkybox->m_pMaterials != NULL)
	{
		delete m_pWorldSkybox->m_pMaterials;
	}

	if(m_pWorldSkybox->m_pMeshTextures)
	{
		for(DWORD j = 0; j < m_pWorldSkybox->m_dwNumMaterials; j++)
		{
			if(m_pWorldSkybox->m_pMeshTextures[j])
			{
				m_pWorldSkybox->m_pMeshTextures[j]->Release();
				m_pWorldSkybox->m_pMeshTextures[j] = NULL;
			}
		}

		delete[] m_pWorldSkybox->m_pMeshTextures;
	}

	if(m_pWorldSkybox->m_pMesh)
	{
		m_pWorldSkybox->m_pMesh->Release();
		m_pWorldSkybox->m_pMesh = NULL;
	}

	delete m_pWorldSkybox;
	m_pWorldSkybox = NULL;
}